package com.yswy.common.wxOrder.V3.wxPayV3Pay;

import com.aliyun.oss.ServiceException;

import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.util.*;
import java.security.Signature;

import com.github.wxpay.sdk.WXPayUtil;
import com.google.gson.Gson;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.yswy.common.utils.OrderNoCenter;
import com.yswy.common.wxOrder.V3.GenerateSignVo;
import com.yswy.common.wxOrder.V3.WxApiType;
import com.yswy.common.wxOrder.V3.WxPayConfigV3;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.math.BigDecimal;

/**
 * @author cv大魔王
 * @version 1.0
 * @date 2022/3/1 16:43
 */
@Slf4j
public class WxPayCommon {


    public static void main(String[] args) throws IOException {

        WxPayConfigV3 wxPayConfig = new WxPayConfigV3();
        wxPayConfig.setMchId("1645167139");// 商户号
        wxPayConfig.setMchSerialNo("5C652203771D07ECB38C5255C18FC4D8A6994EA9");// 商户API证书序列号
        wxPayConfig.setPrivateKeyPath("C:\\Users\\mxy\\Desktop\\wxkey\\1645167139\\apiclient_key.pem");// 商户私钥文件
        wxPayConfig.setCertificateKeyPath("C:\\Users\\mxy\\Desktop\\wxkey\\1645167139\\wechatpay_6BFC5613CA7AE0DBBA924E1C85310DFF47FEB050.pem");
        wxPayConfig.setKey("0F1151762C973622D1D9DEBF516671B2");// APIv3密钥
        wxPayConfig.setAppid("wxd5b74c5ae85bc51e");// APPID
        wxPayConfig.setDomain("https://api.mch.weixin.qq.com"); // 微信服务器地址
        wxPayConfig.setNotifyDomain("https://bkservice.dyh2024.top/payOrder/callbackOrder");// 接收结果通知地址

        //订单ID
        String orderId = OrderNoCenter.getInstance().create();
        WeChatBasePayData basePayData =new WeChatBasePayData();
        basePayData.setOrderId(orderId);
        basePayData.setPrice(new BigDecimal("10"));
        basePayData.setTitle("测试商品");;

        CloseableHttpClient closeableHttpClient = wxPayConfig.getWxPayClient();
        GenerateSignVo generateSignVo = WxPayCommon.wxAppPay(wxPayConfig , basePayData , closeableHttpClient);

        System.out.println("创建微信支付订单："+generateSignVo);
    }
    /**
     * 创建微信支付订单-Native方式
     * 二维码支付
     * @param wxPayConfig 微信配置信息
     * @param basePayData 基础请求信息，商品标题、商家订单id、订单价格
     * @param wxPayClient 微信请求客户端（）
     * @return 微信支付二维码地址
     */
    public static String wxNativePay(WxPayConfigV3 wxPayConfig, WeChatBasePayData basePayData, CloseableHttpClient wxPayClient) {
        // 1.获取请求参数的Map格式
        Map<String, Object> paramsMap = getBasePayParams(wxPayConfig, basePayData);

        // 2.获取请求对象
        HttpPost httpPost = getHttpPost(wxPayConfig, WxApiType.NATIVE_PAY, paramsMap);

        // 3.完成签名并执行请求
        CloseableHttpResponse response = null;
        try {
            response = wxPayClient.execute(httpPost);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException("微信支付请求失败");
        }

        // 4.解析response对象
        HashMap<String, String> resultMap = resolverResponse(response);
        if (resultMap != null) {
            // native请求返回的是二维码链接，前端将链接转换成二维码即可
            return resultMap.get("code_url");
        }
        return null;
    }


    /**
     * 创建微信支付订单-jsapi方式
     *
     * @param wxPayConfig 微信配置信息
     * @param basePayData 基础请求信息，商品标题、商家订单id、订单价格
     * @param openId 通过微信小程序或者公众号获取到用户的openId
     * @param wxPayClient 微信请求客户端（）
     * @return 微信支付二维码地址
     */
    public static GenerateSignVo wxJsApiPay(WxPayConfigV3 wxPayConfig, WeChatBasePayData basePayData, String openId,CloseableHttpClient wxPayClient) {
        // 1.获取请求参数的Map格式
        Map<String, Object> paramsMap = getBasePayParams(wxPayConfig, basePayData);
        // 1.1 添加支付者信息
        Map<String,String> payerMap = new HashMap();
        payerMap.put("openid",openId);
        paramsMap.put("payer",payerMap);

        // 2.获取请求对象
        HttpPost httpPost = getHttpPost(wxPayConfig, WxApiType.JSAPI_PAY, paramsMap);

        // 3.完成签名并执行请求
        CloseableHttpResponse response = null;
        try {
            response = wxPayClient.execute(httpPost);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException("微信支付请求失败");
        }

        // 4.解析response对象
        HashMap<String, String> resultMap = resolverResponse(response);
        if (resultMap != null) {
            // native请求返回的是二维码链接，前端将链接转换成二维码即可
//            return resultMap.get("prepay_id");
            String prepay_id =  resultMap.get("prepay_id") ;
            GenerateSignVo generateSignVo =  generateSignatureJsApi(wxPayConfig, prepay_id , 1);
            return generateSignVo;
        }
        return null;
    }


    /**
     * 创建微信支付订单-APP方式
     *
     * @param wxPayConfig 微信配置信息
     * @param basePayData 基础请求信息，商品标题、商家订单id、订单价格
     * @param wxPayClient 微信请求客户端（）
     * @return 微信支付二维码地址
     */
    public static GenerateSignVo wxAppPay(WxPayConfigV3 wxPayConfig, WeChatBasePayData basePayData, CloseableHttpClient wxPayClient) {
        // 1.获取请求参数的Map格式
        Map<String, Object> paramsMap = getBasePayParams(wxPayConfig, basePayData);

        // 2.获取请求对象
        HttpPost httpPost = getHttpPost(wxPayConfig, WxApiType.APP_PAY, paramsMap);

        // 3.完成签名并执行请求
        CloseableHttpResponse response = null;
        try {
            response = wxPayClient.execute(httpPost);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException("微信支付请求失败");
        }

        // 4.解析response对象
        HashMap<String, String> resultMap = resolverResponse(response);
        if (resultMap != null) {
            // native请求返回的是二维码链接，前端将链接转换成二维码即可
            String prepay_id =  resultMap.get("prepay_id") ;
            GenerateSignVo generateSignVo =  generateSignatureApp(wxPayConfig, prepay_id , 0);
            return generateSignVo;
        }
        return null;
    }

    /**
     * 解析响应数据
     * @param response 发送请求成功后，返回的数据
     * @return 微信返回的参数
     */
    private static HashMap<String, String> resolverResponse(CloseableHttpResponse response) {
        try {
            // 1.获取请求码
            int statusCode = response.getStatusLine().getStatusCode();
            // 2.获取返回值 String 格式
            final String bodyAsString = EntityUtils.toString(response.getEntity());
//            System.out.println("statusCode: "+statusCode);
//            System.out.println("bodyAsString: "+bodyAsString);
            Gson gson = new Gson();
            if (statusCode == 200) {
                // 3.如果请求成功则解析成Map对象返回
                HashMap<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
                return resultMap;
            } else {
                if (StringUtils.isNoneBlank(bodyAsString)) {
                    log.error("微信支付请求失败，提示信息:{}", bodyAsString);
                    // 4.请求码显示失败，则尝试获取提示信息
                    HashMap<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
                    throw new ServiceException(resultMap.get("message"));
                }
                log.error("微信支付请求失败，未查询到原因，提示信息:{}", response);
                // 其他异常，微信也没有返回数据，这就需要具体排查了
                throw new IOException("request failed");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 获取请求对象（Post请求）
     *
     * @param wxPayConfig 微信配置类
     * @param apiType     接口请求地址
     * @param paramsMap   请求参数
     * @return Post请求对象
     */
    private static HttpPost getHttpPost(WxPayConfigV3 wxPayConfig, WxApiType apiType, Map<String, Object> paramsMap) {
        // 1.设置请求地址
        HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(apiType.getType()));

        // 2.设置请求数据
        Gson gson = new Gson();
        String jsonParams = gson.toJson(paramsMap);

        // 3.设置请求信息
        StringEntity entity = new StringEntity(jsonParams, "utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        return httpPost;
    }

    /**
     * 封装基础通用请求参数
     *
     * @param wxPayConfig 微信的配置文件
     * @param basePayData 微信支付基础请求数据
     * @return 封装后的map对象
     */
    private static Map<String, Object> getBasePayParams(WxPayConfigV3 wxPayConfig, WeChatBasePayData basePayData) {
        Map<String, Object> paramsMap = new HashMap<>();
        paramsMap.put("appid", wxPayConfig.getAppid());
        paramsMap.put("mchid", wxPayConfig.getMchId());
        // 如果商品名称过长则截取
        String title = basePayData.getTitle().length() > 62 ? basePayData.getTitle().substring(0, 62) : basePayData.getTitle();
        paramsMap.put("description", title);
        paramsMap.put("out_trade_no", basePayData.getOrderId());
        paramsMap.put("notify_url", wxPayConfig.getNotifyDomain());
        Map<String, Integer> amountMap = new HashMap<>();
//        amountMap.put("total", basePayData.getPrice().multiply(new BigDecimal("100")).intValue());
        amountMap.put("total", basePayData.getPrice().intValue());
        paramsMap.put("amount", amountMap);
//        System.out.println(paramsMap.toString());
        return paramsMap;
    }


    private static GenerateSignVo generateSignatureApp(WxPayConfigV3 wxPayConfig, String prepayId , Integer payType) {
        GenerateSignVo generateSignVo = new GenerateSignVo();
        generateSignVo.setAppId(wxPayConfig.getAppid());
        generateSignVo.setPartnerId(wxPayConfig.getMchId());
        generateSignVo.setPrepayId(prepayId);

        String s = generateSignVo.getAppId()+"\n"+
                generateSignVo.getTimeStamp()+"\n"+
                generateSignVo.getNonceStr()+"\n"+
                generateSignVo.getPrepayId()+"\n";
        try {
            String sign = sign(s.getBytes(StandardCharsets.UTF_8), wxPayConfig.getPrivateKey(wxPayConfig.getPrivateKeyPath()));
            generateSignVo.setSing(sign);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return generateSignVo;
    }


    private static GenerateSignVo generateSignatureJsApi(WxPayConfigV3 wxPayConfig, String prepayId , Integer payType) {
        GenerateSignVo generateSignVo = new GenerateSignVo();
        generateSignVo.setAppId(wxPayConfig.getAppid());
        generateSignVo.setPartnerId(wxPayConfig.getMchId());
        generateSignVo.setPrepayId("prepay_id="+prepayId);
        generateSignVo.setSignType("RSA");
        try {
            String s = generateSignVo.getAppId()+"\n"+
                    generateSignVo.getTimeStamp()+"\n"+
                    generateSignVo.getNonceStr()+"\n"+
                    generateSignVo.getPrepayId()+"\n";
            String sign = sign(s.getBytes(StandardCharsets.UTF_8), wxPayConfig.getPrivateKey(wxPayConfig.getPrivateKeyPath()));
            generateSignVo.setSing(sign);
        } catch (Exception e) {
            e.printStackTrace();
        }
//        if (payType ==0){
//
//            //app支付
//            try {
//                String s = generateSignVo.getAppId()+"\n"+
//                        generateSignVo.getTimeStamp()+"\n"+
//                        generateSignVo.getNonceStr()+"\n"+
//                        prepayId+"\n";
//                String sign = sign(s.getBytes(StandardCharsets.UTF_8), wxPayConfig.getPrivateKey(wxPayConfig.getPrivateKeyPath()));
//                generateSignVo.setSing(sign);
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }else if (payType ==1){
//            //app支付
//            try {
//                String s = generateSignVo.getAppId()+"\n"+
//                        generateSignVo.getTimeStamp()+"\n"+
//                        generateSignVo.getNonceStr()+"\n"+
//                        prepayId+"\n";
//                String sign = sign(s.getBytes(StandardCharsets.UTF_8), wxPayConfig.getPrivateKey(wxPayConfig.getPrivateKeyPath()));
//                generateSignVo.setSing(sign);
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }

        return generateSignVo;
    }
    private static String sign(byte[] message , PrivateKey key) {
        Signature sign = null;
        try {
            sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(key);
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null ;
    }
}

